(论文阅读)Binary-level Directed Fuzzing for Use-After-Free Vulnerabilities
时间:2020
作者:Manh-Dung Nguyen(巴黎萨克雷大学CEA)、Sébastien Bardin、Richard Bonichon
会议:RAID
开源:[1]https://github.com/strongcourage/uafuzz ;[2]https://github.com/strongcourage/uafbench
ABSTRACT
Directed fuzzing聚焦于通过利用额外信息(bug stack trace、补丁或者有风险的操作)来测试代码的特定部分,其重要的应用场景包括漏洞复现、补丁测试以及对静态分析报告的验证。尽管Directed fuzzing最近受到了很多关注,但诸如UAF等难以检测的漏洞仍然没有得到很好的解决,特别是在二进制层面。
我们提出了UAFUZZ,第一个针对UAF漏洞的二进制级别directed greybox fuzzer。该技术包括了一个针对UAF特性设计的Fuzzer,一个轻量级的代码插装(code instrumentation)工具和一个高效的bug处理步骤。
对真实案例中的bug复现进行的实验评估表明,UAFUZZ在故障检测率、暴露时间和bug处理方面明显优于最先进的Directed fuzzer。UAFUZZ在补丁测试中也被证明是有效的,在Perl、GPAC和GNU Patch等程序中发现了30个新的错误(7个CVEs)。最后,我们为社区提供了一个专门用于UAF的大型模糊测试基准,该基准建立在真实代码和真实bug之上。
问题背景
CGF: Coverage-based Greybox Fuzzing
DGF: Directed Greybox Fuzzing
PUT: program under test
Regression Bug: When some bugs are found to be occurring as a result of the bug fix, those are known as regression bugs.
Bug stack traces: sequences of function calls at the time a bug is triggered(KASAN、ASan、VALGRIND)
Noninterference Bug: 原本应该隔离的部分发生了影响,例如多线程的静态条件漏洞
Flaky bug: 不可靠的bug,其结果难以可靠的复现,其根本原因难以分析
Directed fuzzing的应用场景:
- bug reproduction,漏洞复现[25, 28, 42, 61]
- patch testing,补丁测试[25, 51, 59]
- static analysis report verification,静态分析报告验证[31, 49]
1. 触发特定漏洞的必要性
基于应用场景的不同,在Directed fuzzing中目标位置会被bug stack traces、补丁文件或者静态分析报告所引导。本文聚焦于漏洞复现,由于信息缺失和用户隐私问题,通常只有54.9%的错误报告可以被重现,所以特别需要这种方法。即使错误报告中提供了PoC,开发人员在修复的过程中可能仍然需要考虑错误的所有情况,以避免regression bug或不完整的修复。在这种情况下,提供更多的触发错误的输入,对促进和加快bug的修复过程非常重要。
2. 目前fuzzer的局限
目前的grey box fuzzer(无论是否directed)仍然很难找到复杂的漏洞,例如UAF、Noninterference或flaky漏洞,这些漏洞需要满足非常具体的属性的漏洞触发路径。例如,OSSFUZZ 或最近的grey box fuzzer 只发现了少量的UAF。
3. UAF
UAF漏洞相比其他类型的漏洞缺乏解决的技术,并且危害十分严重,如数据损坏、信息泄露和Dos攻击。并且要检测UAF还需要面对两个问题:
状态空间的复杂性:一个UAF的产生需要经过
分配(alloc)
-释放(free)
然后利用(use)
三个顺序过程,在时间和空间上都有限制;隐性性状:UAF漏洞经常没有例如segmentation fault的显性表现,单纯观察崩溃行为的fuzzer就无法发现这种错误。如今流行的内存检测工具:ASan、VALGRIND由于其开销过大不能在fuzzing中使用。
(ps:如果是仅仅漏洞复现,可以利用其产生的报告信息,参考[GREBE| shawdox.github.io])
目前的state-of-the-art:AFLGO和HAWKEYE没法处理UAF并且成本过高。
创新点&贡献
下表是UAFUZZ与state-of-the-art的比较:
创新点与贡献:针对UAF的Directed Fuzzing,比state-of-the-art的方法准确率更高,速度更快;开源发布了针对UAF漏洞的测试benchmark,包括了从公开软件中提取的30个真实的Bug。
方法
1. Bug Trace Flattening
Bug Trace Flattening
指重构从report中提取的bug trace,以下图的report为例:
将上述report生成的3个call trace合并为一个call tree,并将其按照UAF漏洞的执行顺序(i.e alloc->free->use)的顺序生成分支:
如上图所示,该树状图共有三个分支,分别对应了stack trace for the Alloc、stack trace for the free和stack trace for the bad USE的调用路径。按照0到6的顺序遍历该树,就是UAF漏洞的执行过程,漏洞在6处触发。
0 → 1 → 2 → 3(n~alloc~) → 4(n~free~) → 5 → 6(n~use~)
2. Seed Selection
2.1 Similarity Metrics
s代表某个input,T代表report中的目标UAF bug trace,t(s, T )代表s与T的相似度。这里定义了4个测试标准:
- Target prefix t~P~(s, T ):s与T的轨迹覆盖前缀(遇到第一个不同就停止);
- UAF prefix t~3TP~(s, T ):s与T的UAF轨迹覆盖前缀(只关注trace中的UAF事件);
- Target bag t~B~(s, T ):s与T的轨迹覆盖数;
- UAF bag t~3TB~(s, T ):s与T的UAF轨迹覆盖数。
以下图为例,其需要在buf数组的前三个字节填充“AFU”来触发UAF漏洞,当s=’ABUA’时,上述的四个标准值分别为:t~P~(s, T ) = 2,t~3TP~(s, T ) = 1,t~B~(s, T ) = 3,t~3TB~(s, T ) = 2。
将四个metrics结合,得到P-3TP-B:
上述式子代表,衡量Similarity的标准首先是轨迹前缀覆盖,若其相同则次要考虑UAF轨迹的前缀,最后考虑轨迹的整体覆盖率。这样做兼顾了精度与广度,UAFUZZ的默认种子衡量算法使用P-3TP-B。
2.2 Seed Selection Algorithm
如上图所示,种子的选择策略是:要么其与report中的目标bug trace T有着更高的重合度,要么其可以提升代码覆盖率。
2.3 UAF-based Seed Distance
AFL使用原始CFG图,图中边的权重都是1;HAWKEYE通过修改边的权重来引导fuzzer。
如何引导Fuzzer按某种顺序依次运行?这里定义了三个集合:R~alloc,~ R~free~, 和R~use~,分别表示可以执行alloc、free、use操作的函数集合。根据函数调用关系图来判断,例如,如果fa ∈ R~alloc~并且fb ∈ R ~free~ ∩ R~use~,则从a到b的调用则可能触发UAF漏洞。如下图红色的边。
确定了f~a~->f~b~的调用边,通过减少其边的权重来让fuzzer更容易执行它,在本工作的实验中,使用如下权重(β=0.25):
2.4 Cut-edge Coverage Metric
由于对基本块插装的成本过高,HAWKEYE使用对函数插装的方式来跟踪某个种子的运行轨迹(function level)。
本工作提出cut-edge coverage metric,在edge level上衡量关键节点的程序运行轨迹,该方法是basic block level和function level的平衡。
ps:
cut-edge:两个basic block(source和sink)之间的cut edge就是存在一条通过这条edge的路径,可以从source到达sink;
algorithm:算法3、4说明了如何在函数间/函数内部计算识别cut edge;
- 想法:遍历更多cut-edge,更少non-cut edge更容易达到目标轨迹。
2.5 Power Schedule
Power Schedule结合了上述三种方法(target similarity metric t~P~(s, T )、UAF-based seed distance d(s, T )和cut-edge coverage metric e~s~(s, T ))。
2.6 Postprocess and Bug Triage
UAF漏洞发生时一般并不会伴随显式的崩溃,故要检测某个种子是否触发了UAF,需要将其送入bug triager中(例如VALGRIND)检测。因此,当fuzzer生成了大量的种子时,这么做成本很高。
但由于本工作的target similarity metric可以检测种子的指令路径是否包含了三个UAF关键事件,故bug triager只需要检测包含这些事件的种子即可。
代码实现基于AFL和QEMU。
测试结果
baseline:AFL-QEMU
compare:AFLGO、HAWKEYE
benchmark:自行构建